Latviešu

Ceļvedis par TypeScript generics sarežģītu datu tipu pārvaldībai. Apgūstiet sintaksi, priekšrocības un labāko praksi robustām globālām lietojumprogrammām.

TypeScript Generics: Sarežģītu datu tipu apgūšana robustām lietojumprogrammām

TypeScript, kas ir JavaScript virskopa, ļauj izstrādātājiem rakstīt robustāku un uzturējamāku kodu, izmantojot statisko tipēšanu. Viena no tās jaudīgākajām funkcijām ir generics (vispārīgie tipi), kas ļauj rakstīt kodu, kurš var darboties ar dažādiem datu tipiem, vienlaikus saglabājot tipu drošību. Šis ceļvedis sniedz visaptverošu ieskatu TypeScript generics, koncentrējoties uz to pielietojumu sarežģītiem datu tipiem globālās programmatūras izstrādes kontekstā.

Kas ir Generics?

Generics nodrošina veidu, kā rakstīt atkārtoti lietojamu kodu, kas var darboties ar dažādiem tipiem. Tā vietā, lai rakstītu atsevišķas funkcijas vai klases katram tipam, ko vēlaties atbalstīt, jūs varat uzrakstīt vienu funkciju vai klasi, kas izmanto tipu parametrus. Šie tipu parametri ir vietturi faktiskajiem tipiem, kas tiks izmantoti, kad funkcija vai klase tiks izsaukta vai instancēta. Tas ir īpaši noderīgi, strādājot ar sarežģītām datu struktūrām, kurās datu tips var atšķirties.

Generics izmantošanas priekšrocības

Generics pamatsintakse

Generics pamatsintakse ietver leņķa iekavu (< >) izmantošanu, lai deklarētu tipa parametrus. Šiem tipa parametriem parasti tiek doti nosaukumi T, K, V utt., bet jūs varat izmantot jebkuru derīgu identifikatoru. Šeit ir vienkāršs vispārīgas funkcijas piemērs:


function identity<T>(arg: T): T {
  return arg;
}

let myString: string = identity<string>("hello");
let myNumber: number = identity<number>(123);
let myBoolean: boolean = identity<boolean>(true);

console.log(myString); // Output: hello
console.log(myNumber); // Output: 123
console.log(myBoolean); // Output: true

Šajā piemērā <T> deklarē tipa parametru ar nosaukumu T. Funkcija identity pieņem argumentu ar tipu T un atgriež vērtību ar tipu T. Izsaucot funkciju, jūs varat skaidri norādīt tipa parametru (piemēram, identity<string>) vai ļaut TypeScript to noteikt, pamatojoties uz argumenta tipu.

Darbs ar sarežģītiem datu tipiem

Generics kļūst īpaši vērtīgi, strādājot ar sarežģītiem datu tipiem, piemēram, masīviem, objektiem un saskarnēm. Apskatīsim dažus izplatītus scenārijus:

Vispārīgie masīvi

Jūs varat izmantot generics, lai izveidotu funkcijas vai klases, kas darbojas ar dažādu tipu masīviem:


function arrayToString<T>(arr: T[]): string {
  return arr.join(", ");
}

let numberArray: number[] = [1, 2, 3, 4, 5];
let stringArray: string[] = ["apple", "banana", "cherry"];

console.log(arrayToString(numberArray)); // Output: 1, 2, 3, 4, 5
console.log(arrayToString(stringArray)); // Output: apple, banana, cherry

Šeit funkcija arrayToString pieņem masīvu ar tipu T[] un atgriež masīva virknes attēlojumu. Šī funkcija darbojas ar jebkura tipa masīviem, padarot to ļoti atkārtoti lietojamu.

Vispārīgie objekti

Generics var izmantot arī, lai definētu funkcijas vai klases, kas darbojas ar dažādu formu objektiem:


interface Person {
  name: string;
  age: number;
  country: string; // Pievienota valsts globālam kontekstam
}

interface Product {
  id: number;
  name: string;
  price: number;
  currency: string; // Pievienota valūta globālam kontekstam
}

function displayInfo<T extends { name: string }>(item: T): void {
  console.log(`Name: ${item.name}`);
}

let person: Person = { name: "Alice", age: 30, country: "USA" };
let product: Product = { id: 1, name: "Laptop", price: 1200, currency: "USD" };

displayInfo(person); // Output: Name: Alice
displayInfo(product); // Output: Name: Laptop

Šajā piemērā funkcija displayInfo pieņem objektu ar tipu T, kuram jābūt īpašībai name ar tipu string. Klauzula extends { name: string } ir ierobežojums, kas nosaka minimālās prasības tipa parametram T. Tas nodrošina, ka funkcija var droši piekļūt īpašībai name.

Padziļināta Generics lietošana

TypeScript generics piedāvā arī sarežģītākas funkcijas, kas ļauj jums izveidot vēl elastīgāku un jaudīgāku kodu. Apskatīsim dažas no šīm funkcijām:

Vairāki tipu parametri

Jūs varat definēt funkcijas vai klases ar vairākiem tipu parametriem:


function merge<T, U>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 };
}

interface Name {
  firstName: string;
}

interface Age {
  age: number;
}

const person: Name = { firstName: "Bob" };
const details: Age = { age: 42 };

const merged = merge(person, details);
console.log(merged.firstName); // Output: Bob
console.log(merged.age); // Output: 42

Funkcija merge pieņem divus objektus ar tipiem T un U un atgriež jaunu objektu, kas satur abu objektu īpašības. Tas ir jaudīgs veids, kā apvienot datus no dažādiem avotiem.

Vispārīgie ierobežojumi

Kā jau tika parādīts iepriekš, ierobežojumi ļauj sašaurināt tipus, kurus var izmantot ar vispārīgo tipa parametru. Tas nodrošina, ka vispārīgais kods var droši darboties ar norādītajiem tipiem.


interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

loggingIdentity([1, 2, 3]); // Output: 3
loggingIdentity("hello"); // Output: 5
// loggingIdentity(123); // Kļūda: Argument of type 'number' is not assignable to parameter of type 'Lengthwise'.

Funkcija loggingIdentity pieņem argumentu ar tipu T, kuram jābūt īpašībai length ar tipu number. Tas nodrošina, ka funkcija var droši piekļūt īpašībai length.

Vispārīgās klases

Generics var izmantot arī ar klasēm:


class DataStorage<T> {
  private data: T[] = [];

  addItem(item: T) {
    this.data.push(item);
  }

  removeItem(item: T) {
    this.data = this.data.filter(d => d !== item);
  }

  getItems(): T[] {
    return [...this.data];
  }
}

const textStorage = new DataStorage<string>();
textStorage.addItem("apple");
textStorage.addItem("banana");
textStorage.removeItem("apple");
console.log(textStorage.getItems()); // Output: [ 'banana' ]

const numberStorage = new DataStorage<number>();
numberStorage.addItem(1);
numberStorage.addItem(2);
numberStorage.removeItem(1);
console.log(numberStorage.getItems()); // Output: [ 2 ]

Klase DataStorage var glabāt jebkura tipa T datus. Tas ļauj jums izveidot atkārtoti lietojamas datu struktūras, kas ir tipu drošas.

Vispārīgās saskarnes

Vispārīgās saskarnes ir noderīgas, lai definētu kontraktus, kas var darboties ar dažādiem tipiem. Piemēram:


interface Result<T, E> {
  success: boolean;
  data?: T;
  error?: E;
}

interface User {
  id: number;
  username: string;
  email: string;
}

interface ErrorMessage {
  code: number;
  message: string;
}

function fetchUser(id: number): Result<User, ErrorMessage> {
  if (id === 1) {
    return { success: true, data: { id: 1, username: "john.doe", email: "john.doe@example.com" } };
  } else {
    return { success: false, error: { code: 404, message: "User not found" } };
  }
}

const userResult = fetchUser(1);
if (userResult.success) {
  console.log(userResult.data.username);
} else {
  console.log(userResult.error.message);
}

Saskarne Result definē vispārīgu struktūru operācijas iznākuma attēlošanai. Tā var saturēt vai nu datus ar tipu T, vai kļūdu ar tipu E. Tas ir izplatīts modelis asinhronu operāciju vai operāciju, kas var neizdoties, apstrādei.

Palīgtipi un Generics

TypeScript nodrošina vairākus iebūvētus palīgtipus, kas labi darbojas ar generics. Šie palīgtipi var palīdzēt jums pārveidot un manipulēt ar tipiem jaudīgos veidos.

Partial<T>

Partial<T> padara visas tipa T īpašības par neobligātām:


interface Person {
  name: string;
  age: number;
}

type PartialPerson = Partial<Person>;

const partialPerson: PartialPerson = { name: "Alice" }; // Derīgs

Readonly<T>

Readonly<T> padara visas tipa T īpašības par tikai lasāmām:


interface Person {
  name: string;
  age: number;
}

type ReadonlyPerson = Readonly<Person>;

const readonlyPerson: ReadonlyPerson = { name: "Bob", age: 42 };
// readonlyPerson.age = 43; // Kļūda: Cannot assign to 'age' because it is a read-only property.

Pick<T, K>

Pick<T, K> atlasa īpašību kopu K no tipa T:


interface Person {
  name: string;
  age: number;
  email: string;
}

type NameAndAge = Pick<Person, "name" | "age">;

const nameAndAge: NameAndAge = { name: "Charlie", age: 28 };

Omit<T, K>

Omit<T, K> noņem īpašību kopu K no tipa T:


interface Person {
  name: string;
  age: number;
  email: string;
}

type PersonWithoutEmail = Omit<Person, "email">;

const personWithoutEmail: PersonWithoutEmail = { name: "David", age: 35 };

Record<K, T>

Record<K, T> izveido tipu ar atslēgām K un vērtībām ar tipu T:


type CountryCodes = "US" | "CA" | "UK" | "DE" | "FR" | "JP" | "CN" | "IN" | "BR" | "AU"; // Paplašināts saraksts globālam kontekstam
type Currency = "USD" | "CAD" | "GBP" | "EUR" | "JPY" | "CNY" | "INR" | "BRL" | "AUD"; // Paplašināts saraksts globālam kontekstam

type CurrencyMap = Record<CountryCodes, Currency>;

const currencyMap: CurrencyMap = {
  "US": "USD",
  "CA": "CAD",
  "UK": "GBP",
  "DE": "EUR",
  "FR": "EUR",
  "JP": "JPY",
  "CN": "CNY",
  "IN": "INR",
  "BR": "BRL",
  "AU": "AUD",
};

Kartētie tipi

Kartētie tipi ļauj jums pārveidot esošos tipus, iterējot pār to īpašībām. Tas ir jaudīgs veids, kā izveidot jaunus tipus, pamatojoties uz esošajiem. Piemēram, jūs varat izveidot tipu, kas padara visas cita tipa īpašības par tikai lasāmām:


interface Person {
  name: string;
  age: number;
}

type ReadonlyPerson = {
  readonly [K in keyof Person]: Person[K];
};

const readonlyPerson: ReadonlyPerson = { name: "Eve", age: 25 };
// readonlyPerson.age = 26; // Kļūda: Cannot assign to 'age' because it is a read-only property.

Šajā piemērā [K in keyof Person] iterē pār visām saskarnes Person atslēgām, un Person[K] piekļūst katras īpašības tipam. Atslēgvārds readonly padara katru īpašību par tikai lasāmu.

Nosacījuma tipi

Nosacījuma tipi ļauj jums definēt tipus, pamatojoties uz nosacījumiem. Tas ir jaudīgs veids, kā izveidot tipus, kas pielāgojas dažādiem scenārijiem.


type NonNullable<T> = T extends null | undefined ? never : T;

type MaybeString = string | null | undefined;
type StringType = NonNullable<MaybeString>; // string

function getValue<T>(value: T): NonNullable<T> {
  if (value == null) { // Apstrādā gan null, gan undefined
    throw new Error("Value cannot be null or undefined");
  }
  return value as NonNullable<T>;
}

try {
  const validValue = getValue("hello");
  console.log(validValue.toUpperCase()); // Output: HELLO

  const invalidValue = getValue(null); // Šis izsauks kļūdu
  console.log(invalidValue); // Šī rinda netiks sasniegta
} catch (error: any) {
  console.error(error.message); // Output: Value cannot be null or undefined
}

Šajā piemērā tips NonNullable<T> pārbauda, vai T ir null vai undefined. Ja tā ir, tas atgriež never, kas nozīmē, ka tips nav atļauts. Pretējā gadījumā tas atgriež T. Tas ļauj jums izveidot tipus, kas garantēti nav null vērtības.

Labākā prakse Generics izmantošanai

Šeit ir dažas labākās prakses, kas jāpatur prātā, lietojot generics:

Piemēri globālā kontekstā

Apskatīsim dažus piemērus, kā generics var izmantot globālā kontekstā:

Valūtas konvertēšana


interface ConversionRate {
  rate: number;
  fromCurrency: string;
  toCurrency: string;
}

function convertCurrency<T extends ConversionRate>(amount: number, rate: T): number {
  return amount * rate.rate;
}

const usdToEurRate: ConversionRate = { rate: 0.85, fromCurrency: "USD", toCurrency: "EUR" };
const amountInUSD = 100;
const amountInEUR = convertCurrency(amountInUSD, usdToEurRate);
console.log(`${amountInUSD} USD is equal to ${amountInEUR} EUR`); // Output: 100 USD is equal to 85 EUR

Datuma formatēšana


interface DateFormatOptions {
  locale: string;
  options: Intl.DateTimeFormatOptions;
}

function formatDate<T extends DateFormatOptions>(date: Date, format: T): string {
  return date.toLocaleDateString(format.locale, format.options);
}

const currentDate = new Date();

const usDateFormat: DateFormatOptions = { locale: "en-US", options: { year: 'numeric', month: 'long', day: 'numeric' } };
const germanDateFormat: DateFormatOptions = { locale: "de-DE", options: { year: 'numeric', month: 'long', day: 'numeric' } };
const japaneseDateFormat: DateFormatOptions = { locale: "ja-JP", options: { year: 'numeric', month: 'long', day: 'numeric' } };

console.log("US Date: " + formatDate(currentDate, usDateFormat));
console.log("German Date: " + formatDate(currentDate, germanDateFormat));
console.log("Japanese Date: " + formatDate(currentDate, japaneseDateFormat));

Tulkošanas serviss


interface Translation {
  [key: string]: string; // Atļauj dinamiskas valodu atslēgas
}

interface LanguageData<T extends Translation> {
  languageCode: string;
  translations: T;
}

const englishTranslations: Translation = {
  "hello": "Hello",
  "goodbye": "Goodbye",
  "welcome": "Welcome to our website!"
};

const spanishTranslations: Translation = {
  "hello": "Hola",
  "goodbye": "Adiós",
  "welcome": "¡Bienvenido a nuestro sitio web!"
};

const frenchTranslations: Translation = {
  "hello": "Bonjour",
  "goodbye": "Au revoir",
  "welcome": "Bienvenue sur notre site web !"
};


const languageData: LanguageData<typeof englishTranslations>[] = [
  {languageCode: "en", translations: englishTranslations },
  {languageCode: "es", translations: spanishTranslations },
  {languageCode: "fr", translations: frenchTranslations}
];

function translate<T extends Translation>(key: string, languageCode: string, languageData: LanguageData<T>[]): string {
  const lang = languageData.find(lang => lang.languageCode === languageCode);
  if (!lang) {
    return `Translation for ${key} in ${languageCode} not found.`;
  }
  return lang.translations[key] || `Translation for ${key} not found.`;
}

console.log(translate("hello", "en", languageData)); // Output: Hello
console.log(translate("hello", "es", languageData)); // Output: Hola
console.log(translate("welcome", "fr", languageData)); // Output: Bienvenue sur notre site web !
console.log(translate("missingKey", "de", languageData)); // Output: Translation for missingKey in de not found.

Noslēgums

TypeScript generics ir jaudīgs rīks, lai rakstītu atkārtoti lietojamu, tipu drošu kodu, kas var darboties ar sarežģītiem datu tipiem. Izprotot generics pamatsintaksi, papildu funkcijas un labāko praksi, jūs varat ievērojami uzlabot savu TypeScript lietojumprogrammu kvalitāti un uzturējamību. Izstrādājot lietojumprogrammas globālai auditorijai, generics var palīdzēt apstrādāt dažādus datu formātus un kultūras konvencijas, nodrošinot nevainojamu lietotāja pieredzi visiem.